#include "Datalyzer.h"
#include "Method.h"

#include<iostream>
#include<string>

using namespace std;

string& trim(string& in_rString)
{
    // Trim Both leading and trailing spaces  
    size_t startpos = in_rString.find_first_not_of(" \t"); 
    // Find the first character position after excluding leading blank spaces  
    size_t endpos = in_rString.find_last_not_of(" \t"); 
    // Find the first character position from reverse af  
       
    // if all spaces or empty return an empty string  
    if(( string::npos == startpos ) || ( string::npos == endpos))  
    {  
        in_rString = "";  
    }  
    else
    {
        in_rString = in_rString.substr(startpos, endpos-startpos+1 ); 
    }

    return in_rString;
}

void splitAsNameAndType(string& in_sString, string& in_sName, string& in_sType)
{
    int nLen;
    int nSplitPos;

    trim(in_sString);
    nSplitPos = in_sString.find_last_of("\t ");
    nLen = in_sString.size();

    if(string::npos == nSplitPos)
        in_sType = in_sString;
    else
    {
        in_sName = in_sString.substr(nSplitPos, nLen -nSplitPos);
        in_sType = in_sString.substr(0,nSplitPos);
    }

    trim(in_sName);
    trim(in_sType);
}

Datalyzer::Datalyzer(void)
{
	m_pxLookaheadParen = NULL;
	
	m_bMatchActiveVariable= false;
	m_bMatchActiveTypeDefiner = false;
	m_bThroughTypedefKeyword = false;

	m_asDataTypeSpecifier.push_back("auto");
	m_asDataTypeSpecifier.push_back("bool");
	m_asDataTypeSpecifier.push_back("char");
	m_asDataTypeSpecifier.push_back("double");
	m_asDataTypeSpecifier.push_back("float");
	m_asDataTypeSpecifier.push_back("int");
	m_asDataTypeSpecifier.push_back("long");
	m_asDataTypeSpecifier.push_back("short");
	m_asDataTypeSpecifier.push_back("wchar_t");

	m_nSerialNumber=0;
	m_nLinenumber=1;

}

Datalyzer::~Datalyzer(void)
{
}

void Datalyzer::onNewToken(Token& inToken)
{
	char cDelimiter  = char(inToken);

	if(cDelimiter=='\n')
		m_nLinenumber++;

	Scope::Type eScopeType = m_pxCurrentScope->getScopeType();
	if(eScopeType == Scope::eScopeCommentC) return;
	if(eScopeType == Scope::eScopeCommentCPP) return;
	if(eScopeType == Scope::eScopePreprocessor) return;
	if(eScopeType == Scope::eScopeLiteral) return;

	if((eScopeType == Scope::eScopeGlobal) ||
		(eScopeType == Scope::eScopeBlock))
		//Variables and types cannot be defined in other scopes, there it is a typecast...
	{
		if(isDataTypeDefiner(inToken.m_sValue))
		{
			if(inToken.m_sValue == "typedef") //Special case
				m_bThroughTypedefKeyword = true;

			m_bMatchActiveTypeDefiner = true;
		}
		else if(isDataTypeSpecifier(inToken.m_sValue))
			m_bMatchActiveVariable=true;

	}

	if(m_bMatchActiveTypeDefiner)
		matchTypeDefiner(inToken);

	else if(m_bMatchActiveVariable) 
		matchVariable(inToken);

}

void Datalyzer::matchTypeDefiner(Token& inToken)
{
	bool bIsDefiner = false;
	char cDelimiter = char(inToken);

	/* Typedefiner logic */	
	switch(cDelimiter)
	{
	case';':
		if(m_bThroughTypedefKeyword)
		{
			bIsDefiner = true;
			m_bThroughTypedefKeyword = false; //reset
		}
	case'{':
		if(!m_bThroughTypedefKeyword)
			bIsDefiner = true;
		break;
	}

	if(bIsDefiner)
	{
		std::string sTypeDefiner = m_pxCurrentScope->m_asSubstream.back();
		std::string sTypeDefinerType;
		std::string sTypeDefinerName;
		
		splitAsNameAndType(sTypeDefiner,sTypeDefinerName,sTypeDefinerType);

		m_asDataTypeSpecifier.push_back(sTypeDefinerName);
		cout<<"Type:"<<sTypeDefinerName<<":"<<sTypeDefinerType<<":"<<m_nLinenumber<<endl;

		m_bMatchActiveTypeDefiner = false;
	}
}

void Datalyzer::matchVariable(Token& inToken)
{
	bool bIsVariable = false;
	char cDelimiter = char(inToken);

	/* Variable logic */	
	switch(cDelimiter)
	{
	case';':
		if (m_pxLookaheadParen)
		{
			bIsVariable = false;
			//std::string sFunction = m_pxCurrentScope->m_asSubstream.back();
			//std::string sFunctionType;
			//std::string sFunctionName;
			//
			//splitAsNameAndType(sFunction,sFunctionName,sFunctionType);

			//m_asPrototype.push_back(sFunctionName);
			//m_nSerialNumber++;
			//cout<<"Prototype:"<<sFunctionName<<":"<<sFunctionType<<":"<<m_nLinenumber<<endl;
		}
		else
		{
			bIsVariable = true;
		}

		m_bMatchActiveVariable = false;
		break;

	case'=':
		bIsVariable = true;
		m_bMatchActiveVariable = false;
		break;
	}

	if(bIsVariable)
	{
		std::string sVariable = m_pxCurrentScope->m_asSubstream.back();
		std::string sVariableType;
		std::string sVariableName;
		
		splitAsNameAndType(sVariable,sVariableName,sVariableType);

		//TODO: Type suffix may have more, array[] etc. Can be done in more general way. Hence deferring.
		//int n=m_pxCurrentScope->m_apxChildren.size();
		//for(int j=0;j<n;j++)
		//	sVariableType+=m_pxCurrentScope->m_apxChildren[i]->m_asSubstream.back();

		m_asVariable.push_back(sVariableName);
		m_nSerialNumber++;
		cout<<m_nSerialNumber<<":"<<sVariableName<<":"<<sVariableType<<":"<<m_nLinenumber<<endl;
	}
}

void Datalyzer::onScopeEntry(Scope& inNewScope, Scope& inOldScope)
{
    m_pxCurrentScope = &inNewScope;
    Scope::Type eNewScope = inNewScope.m_eType;

}

void Datalyzer::onScopeExit(Scope& inNewScope, Scope& inOldScope)
{
	if(Scope::eScopeParen == inOldScope.getScopeType())
	{
		m_pxLookaheadParen = &inOldScope;
	}
	else
	{
		m_pxLookaheadParen = NULL;
	}

	m_pxCurrentScope = &inNewScope;
    Scope::Type eNewScope = inNewScope.m_eType;

}


bool Datalyzer::isDataTypeSpecifier(const string& s)
{
	for (size_t i = 0; i < m_asDataTypeSpecifier.size(); i++)
		if (m_asDataTypeSpecifier[i] == s)
			return true;

	return false;
}

bool Datalyzer::isDataTypeDefiner(const std::string& s)
{
	static const char* asKeywords[] = {
		"class",
		"enum",
		"namespace",
		"struct",
		"typedef",
		"union"
	};

	for (int i = 0; i < sizeof(asKeywords) / sizeof(char*); i++)
		if (string(asKeywords[i]) == s)
			return true;

	return false;

}

bool isBlockKeyword(const std::string& s)
{
	static const char* asKeywords[] = {
		"catch",
		"class",
		"do",
		"else",
		"enum",
		"for",
		"if",
		"namespace",
		"struct",
		"switch",
		"template",
		"throw",
		"try",
		"while"
	};

	for (int i = 0; i < sizeof(asKeywords) / sizeof(char*); i++)
		if (string(asKeywords[i]) == s)
			return true;

	return false;

}

bool Datalyzer::isKeyword(const string& s)
{
	//TODO: Change to map-based
	static const char* asKeywords[] = {
		"and",
		"and_eq",
		"asm",
		"auto",
		"bitand",
		"bitor",
		"bool",
		"break",
		"case",
		"catch",
		"char",
		"class",
		"compl",
		"const",
		"const_cast",
		"continue",
		"default",
		"delete",
		"do",
		"double",
		"dynamic_cast",
		"else",
		"enum",
		"explicit",
		"export",
		"extern",
		"false",
		"float",
		"for",
		"friend",
		"goto",
		"if",
		"inline",
		"int",
		"long",
		"mutable",
		"namespace",
		"new",
		"not",
		"not_eq",
		"operator",
		"or",
		"or_eq",
		"private",
		"protected",
		"public",
		"register",
		"reinterpret_cast",
		"return",
		"short",
		"signed",
		"sizeof",
		"static",
		"static_cast",
		"struct",
		"switch",
		"template",
		"this",
		"throw",
		"true",
		"try",
		"typedef",
		"typeid",
		"typename",
		"union",
		"unsigned",
		"using",
		"virtual",
		"void",
		"volatile",
		"wchar_t",
		"while",
		"xor",
		"xor_eq"
	};

	for (int i = 0; i < sizeof(asKeywords) / sizeof(char*); i++)
		if (string(asKeywords[i]) == s)
			return true;

	return false;
}

void Datalyzer::dump(MinesweeperOptions* pxOpt)
{

}
